package handler

import (
	"context"
	"fmt"
	"github.com/golang/protobuf/ptypes/empty"
	"github.com/golang/protobuf/ptypes/wrappers"
	"github.com/jinzhu/gorm"

	context2 "kop/framework/context"
	"kop/framework/errors"
	"kop/libs/database"
	"kop/libs/redis"
	"kop/modules/item/configuration"
	"kop/modules/item/consts"
	"kop/modules/ranking/entity"
	"kop/modules/ranking/models"
	"kop/pb"
	"kop/pb/service"
	"kop/util/rand"

	itemC "kop/modules/item/client"
	questC "kop/modules/quest/client"
	roleC "kop/modules/role/client"
)

type RankingServiceHandler struct {
	context2.Context
}

func (h *RankingServiceHandler) Kings(ctx context.Context, _ *empty.Empty, resp *pb.KingsResp) error {

	var kings = models.GetKings(database.DB, h.GetServerID(ctx))
	resp.Kings = make(map[int32]*pb.King, len(kings))

	var roleIDs = make([]int32, 0, len(kings))
	for _, v := range kings {
		roleIDs = append(roleIDs, v.RoleID)
	}

	var roles = roleC.GetRoles(roleIDs)
	for _, v := range kings {
		resp.Kings[v.ItemID] = &pb.King{
			RoleID:   v.RoleID,
			Name:     roles[v.RoleID].Name,
			Sex:      roles[v.RoleID].Sex,
			Clothing: roles[v.RoleID].Clothing,
		}
	}

	resp.Ok = 今日是否膜拜海盗王(h.GetRoleID(ctx))
	return nil
}

func (h *RankingServiceHandler) GetKing(ctx context.Context, req *wrappers.Int32Value, resp *pb.GetKingResp) error {

	var king, ok = models.GetKing(database.DB, req.Value, h.GetServerID(ctx))
	if !ok {
		return errors.ParamError
	}

	var role = roleC.GetRole(king.RoleID)

	resp.King = &pb.King{
		RoleID:      king.RoleID,
		Name:        role.Name,
		Sex:         role.Sex,
		Clothing:    role.Clothing,
		Value:       models.InitPopularity(database.DB, king.RoleID, h.GetServerID(ctx)).Value,
		Declaration: roleC.GetDeclaration(king.RoleID),
	}

	return nil
}

func (h *RankingServiceHandler) UseRing(ctx context.Context, req *wrappers.Int32Value, resp *pb.Item) (err error) {

	var roleID, serverID = h.GetRoleID(ctx), h.GetServerID(ctx)

	var item = itemC.GetItem(roleID, req.Value)
	if item == nil || item.ItemType != int32(configuration.KingRing) {
		return errors.ParamError
	}

	if item, err = itemC.DelItem(roleID, 0, item.ItemID, 1); err != nil {
		return err
	} else {
		*resp = *item
	}

	var session = database.DB.Begin()
	if session.Error != nil {
		return session.Error
	}

	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
		if err != nil {
			session.Rollback()
		}
	}()

	var king, ok = models.GetKing(session, item.ItemID, serverID)
	if ok {
		king.SetRoleID(roleID)
		models.SaveKing(session, &king)
	} else {
		king = models.CreateKing(session, item.ItemID, serverID, roleID)
	}

	models.InitPopularity(session, roleID, serverID)
	return session.Commit().Error
}

func (h *RankingServiceHandler) WorshipKing(ctx context.Context, req *wrappers.Int32Value, resp *pb.AwardItem) error {

	var data, ok = models.GetKing(database.DB, req.Value, h.GetServerID(ctx))
	if !ok && configuration.GetByItemID(req.Value).Type != configuration.KingRing {

		return errors.ParamError
	}

	var roleID = h.GetRoleID(ctx)
	if 今日是否膜拜海盗王(roleID) {
		// 已膜拜过
		return nil
	} else {
		保存今日已膜拜海盗王(roleID)
	}

	var diamond = roleC.WorshipDiamond(roleID)

	var n = redis.GetTodayNumber(fmt.Sprintf("WorshipLuck%d", roleID))
	var luck = n < int(roleC.GetVIP(roleID).Benediction)
	if luck && rand.IsWin(0.15) {

		diamond += 100

		redis.SetTodayNumber(fmt.Sprintf("WorshipLuck%d", roleID), n+1)
	}

	resp.RawItem = &pb.RawItem{ItemID: consts.Diamond, Number: diamond}
	resp.Item = itemC.AddItem(roleID, consts.Diamond, diamond)

	if ok {
		database.DB.Model(&entity.Popularity{}).Where("role_id = ?", data.RoleID).UpdateColumn("value", gorm.Expr("value + ?", 1))
	}

	// quest , daily quest
	go questC.Worship(roleID)
	return nil
}

func (h *RankingServiceHandler) WorshipRanking(ctx context.Context, req *wrappers.Int32Value, resp *pb.AwardItem) error {

	if req.Value != 1 && req.Value != 2 && req.Value != 3 && req.Value != 4 {
		return errors.ParamError
	}

	var roleID = h.GetRoleID(ctx)
	if todayWorshipRanking(req.Value, roleID) {
		// 已膜拜过
		return nil
	} else {
		redis.SetTodayNumber(fmt.Sprintf("WorshipRanking_%d_%d", req.Value, roleID), 1)
	}

	var diamond int32 = 10

	var n = redis.GetTodayNumber(fmt.Sprintf("WorshipLuck%d", roleID))
	var luck = n < int(roleC.GetVIP(roleID).Benediction)
	if luck && rand.IsWin(0.15) {

		redis.SetTodayNumber(fmt.Sprintf("WorshipLuck%d", roleID), n+1)
	}

	resp.RawItem = &pb.RawItem{ItemID: consts.Diamond, Number: diamond}
	resp.Item = itemC.AddItem(roleID, consts.Diamond, diamond)

	// daily quest
	questC.DailyQuestWorshipRanking(roleID)
	return nil
}

func (h *RankingServiceHandler) Ranking(_ context.Context, req *wrappers.Int32Value, resp *service.RankingPointsList) error {

	var data []entity.Popularity

	database.DB.Where("server_id = ?", req.Value).Order("value DESC, updated_at ASC").Limit(200).Find(&data)

	resp.Ranking = make([]*service.RankingPoints, 0, len(data))
	for _, v := range data {
		resp.Ranking = append(resp.Ranking, &service.RankingPoints{RoleID: v.RoleID, Points: int64(v.Value)})
	}

	return nil
}

func (h *RankingServiceHandler) MyRanking(ctx context.Context, _ *empty.Empty, resp *pb.RankingNumber) error {

	var data = entity.Popularity{RoleID: h.GetRoleID(ctx)}
	if err := database.DB.First(&data).Error; err == gorm.ErrRecordNotFound {
		return nil
	}

	var number int32
	database.DB.Model(&data).Where("server_id = ? AND value > ?", h.GetServerID(ctx), data.Value).Count(&number)

	resp.Points = int64(data.Value)
	resp.Number = number + 1
	return nil
}

func (h *RankingServiceHandler) IsWorship(ctx context.Context, req *wrappers.Int32Value, resp *wrappers.BoolValue) error {

	resp.Value = todayWorshipRanking(req.Value, h.GetRoleID(ctx))
	return nil
}

func (h *RankingServiceHandler) IsWorshipKing(ctx context.Context, _ *empty.Empty, resp *wrappers.BoolValue) error {

	resp.Value = 今日是否膜拜海盗王(h.GetRoleID(ctx))
	return nil
}

func 今日是否膜拜海盗王(roleID int32) bool {
	return redis.GetTodayNumber(fmt.Sprintf("WorshipKing_%d", roleID)) != 0
}

func 保存今日已膜拜海盗王(roleID int32) {
	redis.SetTodayNumber(fmt.Sprintf("WorshipKing_%d", roleID), 1)
}

func todayWorshipRanking(itemID, roleID int32) bool {
	return redis.GetTodayNumber(fmt.Sprintf("WorshipRanking_%d_%d", itemID, roleID)) != 0
}
